---
stage: Enablement
group: Infrastructure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---

# Feature Categorization

> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/269) in GitLab 13.2.

Each Sidekiq worker, Batched Background migrations, controller action, [test example](../testing_guide/best_practices.md#feature-category-metadata) or API endpoint
must declare a `feature_category` attribute. This attribute maps each
of these to a [feature category](https://about.gitlab.com/handbook/product/categories/). This
is done for error budgeting, alert routing, and team attribution.

The list of feature categories can be found in the file `config/feature_categories.yml`.
This file is generated from the
[`stages.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml)
data file used in the GitLab Handbook and other GitLab resources.

## Updating `config/feature_categories.yml`

Occasionally new features will be added to GitLab stages, groups, and
product categories. When this occurs, you can automatically update
`config/feature_categories.yml` by running
`scripts/update-feature-categories`. This script will fetch and parse
[`stages.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml)
and generate a new version of the file, which needs to be committed to
the repository.

The [Scalability team](https://about.gitlab.com/handbook/engineering/infrastructure/team/scalability/)
currently maintains the `feature_categories.yml` file. They will automatically be
notified on Slack when the file becomes outdated.

## Sidekiq workers

The declaration uses the `feature_category` class method, as shown below.

```ruby
class SomeScheduledTaskWorker
  include ApplicationWorker

  # Declares that this worker is part of the
  # `continuous_integration` feature category
  feature_category :continuous_integration

  # ...
end
```

The feature categories specified using `feature_category` should be
defined in
[`config/feature_categories.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/feature_categories.yml). If
not, the specs will fail.

### Excluding Sidekiq workers from feature categorization

A few Sidekiq workers, that are used across all features, cannot be mapped to a
single category. These should be declared as such using the
`feature_category :not_owned`
declaration, as shown below:

```ruby
class SomeCrossCuttingConcernWorker
  include ApplicationWorker

  # Declares that this worker does not map to a feature category
  feature_category :not_owned # rubocop:disable Gitlab/AvoidFeatureCategoryNotOwned

  # ...
end
```

When possible, workers marked as "not owned" use their caller's
category (worker or HTTP endpoint) in metrics and logs.
For instance, `ReactiveCachingWorker` can have multiple feature
categories in metrics and logs.

## Batched background migrations

Long-running migrations (as per the [time limits guidelines](../migration_style_guide.md#how-long-a-migration-should-take))
are pulled out as [batched background migrations](../database/batched_background_migrations.md).
They should define a `feature_category`, like this:

```ruby
# File name: lib/gitlab/background_migration/my_background_migration_job.rb

class MyBackgroundMigrationJob < BatchedMigrationJob
  feature_category :gitaly

  #...
end
```

NOTE:
[`RuboCop::Cop::BackgroundMigration::FeatureCategory`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/rubocop/cop/background_migration/feature_category.rb) cop ensures a valid `feature_category` is defined.

## Rails controllers

Specifying feature categories on controller actions can be done using
the `feature_category` class method.

A feature category can be specified on an entire controller
using:

```ruby
class Boards::ListsController < ApplicationController
  feature_category :kanban_boards
end
```

The feature category can be limited to a list of actions using the
second argument:

```ruby
class DashboardController < ApplicationController
  feature_category :team_planning, [:issues, :issues_calendar]
  feature_category :code_review_workflow, [:merge_requests]
end
```

These forms cannot be mixed: if a controller has more than one category,
every single action must be listed.

### Excluding controller actions from feature categorization

In the rare case an action cannot be tied to a feature category this
can be done using the `not_owned` feature category.

```ruby
class Admin::LogsController < ApplicationController
  feature_category :not_owned
end
```

### Ensuring feature categories are valid

The `spec/controllers/every_controller_spec.rb` will iterate over all
defined routes, and check the controller to see if a category is
assigned to all actions.

The spec also validates if the used feature categories are known. And if
the actions used in configuration still exist as routes.

## API endpoints

The [GraphQL API](../../api/graphql/index.md) is currently categorized
as `not_owned`. For now, no extra specification is needed. For more
information, see
[`gitlab-com/gl-infra/scalability#583`](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/583/).

Grape API endpoints can use the `feature_category` class method, like
[Rails controllers](#rails-controllers) do:

```ruby
module API
  class Issues < ::API::Base
    feature_category :team_planning
  end
end
```

The second argument can be used to specify feature categories for
specific routes:

```ruby
module API
  class Users < ::API::Base
    feature_category :user_profile, ['/users/:id/custom_attributes', '/users/:id/custom_attributes/:key']
  end
end
```

Or the feature category can be specified in the action itself:

```ruby
module API
  class Users < ::API::Base
    get ':id', feature_category: :user_profile do
    end
  end
end
```

As with Rails controllers, an API class must specify the category for
every single action unless the same category is used for every action
within that class.

## RSpec Examples

You must set feature category metadata for each RSpec example. This information is used for flaky test
issues to identify the group that owns the feature.

The `feature_category` should be a value from [`config/feature_categories.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/feature_categories.yml).

The `feature_category` metadata can be set:

- [In the top-level `RSpec.describe` blocks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104274/diffs#6bd01173381e873f3e1b6c55d33cdaa3d897156b_5_5).
- [In `describe` blocks](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104274/diffs#a520db2677a30e7f1f5593584f69c49031b894b9_12_12).

Consider splitting the file in the case there are multiple feature categories identified in the same file.

Example:

 ```ruby
 RSpec.describe Admin::Geo::SettingsController, :geo, feature_category: :geo_replication do
 ```

For examples that don't have a `feature_category` set we add a warning when running them in local environment.

To disable the warning use `RSPEC_WARN_MISSING_FEATURE_CATEGORY=false` when running RSpec tests:

```shell
RSPEC_WARN_MISSING_FEATURE_CATEGORY=false bin/rspec spec/<test_file>
```

Additionally, we flag the offenses via `RSpec/MissingFeatureCategory` RuboCop rule.

### Tooling feature category

For Engineering Productivity internal tooling we use `feature_category: :tooling`.

For example in [`spec/tooling/danger/specs_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/tooling/danger/specs_spec.rb#L12).

### Shared feature category

For features that support developers and they are not specific to a product group we use `feature_category: :shared`
For example [`spec/lib/gitlab/job_waiter_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/lib/gitlab/job_waiter_spec.rb)

### Admin section

Adding feature categories is equally important when adding new parts to the Admin section. Historically, Admin sections were often marked as `not_owned` in the code. Now
you must ensure each new addition to the Admin section is properly annotated using `feature_category` notation.
